import tkinter as tk
from tkinter import filedialog
from pml.Parser.lexer.lexer import Lexer
from pml.Parser.lexer.position import Position
from pml.Parser import ASTBuilder
from pml.Bytecode.BytecodeBuilder import BytecodeBuilder
from pml.Eval import frame as Frame, stack as Stack, Runner
from pml.PML.tool import CallFunction
import pickle, os
import terminal

FONT = '仿宋'

root = tk.Tk()
root.title('PML Notepad')
root.geometry('800x600')

scrollbar = tk.Scrollbar(root, orient='vertical')
scrollbar.pack(side=tk.RIGHT, fill='both')

text = tk.Text(root, yscrollcommand=scrollbar.set, font=(FONT, 15))
text.place(x=5, y=0, width=775, height=570)

scrollbar.config(command=text.yview)

foot_file = tk.Label(root, text='new', anchor='e', font=(FONT, 10))
foot_file.place(x=140, y=570, width=650, height=30)

foot_position = tk.Label(root, text='line: 0 column: 0', anchor='w', font=(FONT, 12))
foot_position.place(x=0, y=570, width=150, height=30)

def cursorPosition(event):
    line, column = event.widget.index("insert").split(".")
    foot_position['text'] = f'line: {line} column: {column}'
text.bind('<KeyPress>', cursorPosition)
text.bind('<KeyRelease>', cursorPosition)
text.bind('<ButtonPress>', cursorPosition)
text.bind('<ButtonRelease>', cursorPosition)

menu = tk.Menu(root, font=FONT)
root.config(menu=menu)

fileMenu = tk.Menu(menu, tearoff=0)
menu.add_cascade(label='file', menu=fileMenu)

def openFile():
    path = filedialog.askopenfile()
    text.delete(1.0, tk.END)
    text.insert(1.0, path.read())
    path.close()
    foot_file['text'] = path.name
fileMenu.add_command(label='open', command=openFile)

def saveFile():
    path = filedialog.asksaveasfile()
    path.write(text.get(1.0, tk.END))
    path.close()
fileMenu.add_command(label='save', command=saveFile)

testMenu = tk.Menu(menu, tearoff=0)
menu.add_cascade(label='test', menu=testMenu)

def Term(file):
    term = terminal.Terminal()

    lexer = Lexer(Position(file, open(file), -1, 0, 1))
    tokens = lexer.lexer(None, lexer, position=None)
    term.write(None, term, None, tokens.__repr__() + '\n')

    builder = ASTBuilder(iter(tokens), depth=1)
    builder.getNextToken()
    ast = builder.buildAST(None, builder, position=None)
    term.write(None, term, None, ast.__repr__() + '\n')

    builder = BytecodeBuilder(ast)
    bytecodes = builder.buildBytecode(None, builder).obj.value

    for bytecode in bytecodes:
        term.write(None, term, None, bytecode.__repr__() + '\n')
    term.write(None, term, None, '\n')

    with open(os.path.splitext(file)[0] + '.pmc', 'wb') as o:
        pickle.dump(bytecodes, o)

    return term

def compile():
    Term(foot_file['text']).run()
testMenu.add_command(label='compile', command=compile)

def run():
    term = Term(foot_file['text'])

    file = os.path.split(foot_file['text'])[0]

    with open(os.path.splitext(foot_file['text'])[0] + '.pmc', 'rb') as o:
        bytecodes = pickle.load(o)

    variableTable = {}
    variableTable['this'] = variableTable
    const = {}

    stack = Stack.Stack()
    frame = Frame.Frame(file, bytecodes, stack, variableTable, const, stdio=term)
    this = Frame.Objects.object.typeobject('<global>')
    for name in variableTable:
        this.methods[name] = variableTable[name]
    runner = Runner.Runner(frame, position=os.path.split(os.path.realpath(file))[0], this=this)
    runner.run(None, runner, position=file)

    if runner.error is not None:
        error = CallFunction.CallFunction(frame, runner.error, runner.error.getMember('__throw__'), runner.errorFrame)
        term.write(None, term, None, error)

    term.run()
testMenu.add_command(label='run', command=run)

root.mainloop()
